001 /* 002 * Copyright 2006 Stephen J. McConnell. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.metro.builder; 020 021 import java.io.IOException; 022 import java.io.OutputStream; 023 import java.io.OutputStreamWriter; 024 import java.io.Writer; 025 import java.net.URI; 026 027 import javax.xml.XMLConstants; 028 029 import net.dpml.component.ActivationPolicy; 030 import net.dpml.component.Directive; 031 032 import net.dpml.lang.Value; 033 import net.dpml.lang.ValueEncoder; 034 035 import net.dpml.metro.data.ContextDirective; 036 import net.dpml.metro.data.CategoryDirective; 037 import net.dpml.metro.data.CategoriesDirective; 038 import net.dpml.metro.data.ComponentDirective; 039 import net.dpml.metro.data.LookupDirective; 040 import net.dpml.metro.data.ValueDirective; 041 042 import net.dpml.metro.info.LifestylePolicy; 043 import net.dpml.metro.info.CollectionPolicy; 044 import net.dpml.metro.info.PartReference; 045 import net.dpml.metro.info.Priority; 046 047 import net.dpml.util.Encoder; 048 049 /** 050 * Component part handler. 051 * 052 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 053 * @version 1.0.1 054 */ 055 public class ComponentEncoder extends ComponentConstants implements Encoder 056 { 057 private static final String XML_HEADER = "<?xml version=\"1.0\"?>"; 058 private static final String TYPE_SCHEMA_URN = "link:xsd:dpml/lang/dpml-type#1.0"; 059 private static final String STATE_SCHEMA_URN = "link:xsd:dpml/lang/dpml-state#1.0"; 060 private static final String PART_SCHEMA_URN = "link:xsd:dpml/lang/dpml-part#1.0"; 061 private static final String COMPONENT_SCHEMA_URN = "link:xsd:dpml/lang/dpml-component#1.0"; 062 private static final String PARTIAL_COMPONENT_HEADER = 063 "<component xmlns=\"" 064 + COMPONENT_SCHEMA_URN 065 + "\"" 066 + "\n xmlns:xsi=\"" 067 + XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI 068 + "\"\n xmlns:part=\"" 069 + PART_SCHEMA_URN 070 + "\"\n xmlns:type=\"" 071 + TYPE_SCHEMA_URN 072 + "\"\n xmlns:component=\"" 073 + COMPONENT_SCHEMA_URN 074 + "\""; 075 076 private static final ValueEncoder VALUE_ENCODER = new ValueEncoder(); 077 078 /** 079 * Export a component directive to an output stream as XML. 080 * @param directive the component directive 081 * @param output the output stream 082 * @exception IOException if an IO error occurs 083 */ 084 public void export( ComponentDirective directive, OutputStream output ) throws IOException 085 { 086 final Writer writer = new OutputStreamWriter( output ); 087 088 writer.write( XML_HEADER ); 089 writer.write( "\n\n" ); 090 writer.write( PARTIAL_COMPONENT_HEADER ); 091 writeAttributes( writer, directive, "" ); 092 writeBody( writer, directive, " " ); 093 writer.write( "\n" ); 094 writer.write( "</component>" ); 095 writer.write( "\n" ); 096 writer.flush(); 097 output.close(); 098 } 099 100 /** 101 * Export a component directive to an output stream as XML. 102 * @param writer the print writer 103 * @param object the object to encode 104 * @param pad character offset 105 * @exception IOException if an IO error occurs 106 */ 107 public void encode( Writer writer, Object object, String pad ) throws IOException 108 { 109 if( object instanceof ComponentDirective ) 110 { 111 writeTaggedComponent( writer, (ComponentDirective) object, null, pad, true ); 112 } 113 else 114 { 115 final String error = 116 "Encoding subject is not recognized." 117 + "\nClass: " + object.getClass().getName(); 118 throw new IllegalArgumentException( error ); 119 } 120 } 121 122 /** 123 * Export a component directive to an output stream as XML. 124 * @param writer the print writer 125 * @param directive the component directive 126 * @param pad character offset 127 * @exception IOException if an IO error occurs 128 */ 129 public void writeComponent( 130 Writer writer, ComponentDirective directive, String pad ) throws IOException 131 { 132 writeTaggedComponent( writer, directive, null, pad ); 133 } 134 135 /** 136 * Export a tagged component directive to an output stream as XML. 137 * @param writer the print writer 138 * @param directive the component directive 139 * @param key the key identifying the component 140 * @param pad character offset 141 * @exception IOException if an IO error occurs 142 */ 143 public void writeTaggedComponent( 144 Writer writer, ComponentDirective directive, String key, String pad ) throws IOException 145 { 146 writeTaggedComponent( writer, directive, key, pad, true ); 147 } 148 149 /** 150 * Export a tagged component directive to an output stream as XML. 151 * @param writer the print writer 152 * @param directive the component directive 153 * @param key the key identifying the component 154 * @param pad character offset 155 * @param flag true if the xml namespace should be included 156 * @exception IOException if an IO error occurs 157 */ 158 public void writeTaggedComponent( 159 Writer writer, ComponentDirective directive, String key, String pad, boolean flag ) throws IOException 160 { 161 writer.write( "\n" + pad + "<component" ); 162 if( flag ) 163 { 164 writer.write( " xmlns=\"" + COMPONENT_SCHEMA_URN + "\"" ); 165 } 166 if( null != key ) 167 { 168 writer.write( " key=\"" + key + "\"" ); 169 } 170 writer.write( "\n" + pad + " " ); 171 writeAttributes( writer, directive, pad + " " ); 172 writeBody( writer, directive, pad + " " ); 173 writer.write( "\n" + pad + "</component>" ); 174 } 175 176 void writeAttributes( 177 Writer writer, ComponentDirective directive, String pad ) throws IOException 178 { 179 String classname = directive.getClassname(); 180 if( null != classname ) 181 { 182 writer.write( " type=\"" + classname + "\"" ); 183 } 184 URI uri = directive.getBaseURI(); 185 if( null != uri ) 186 { 187 writer.write( " uri=\"" + uri.toASCIIString() + "\"" ); 188 } 189 String name = directive.getName(); 190 if( null != name ) 191 { 192 writer.write( "\n" + pad + " name=\"" + name + "\"" ); 193 } 194 LifestylePolicy lifestyle = directive.getLifestylePolicy(); 195 if( null != lifestyle ) 196 { 197 writer.write( "\n" + pad + " lifestyle=\"" + lifestyle.getName() + "\"" ); 198 } 199 CollectionPolicy collection = directive.getCollectionPolicy(); 200 if( null != collection ) 201 { 202 writer.write( "\n" + pad + " collection=\"" + collection.getName() + "\"" ); 203 } 204 ActivationPolicy activation = directive.getActivationPolicy(); 205 if( null != activation ) 206 { 207 writer.write( "\n" + pad + " activation=\"" + activation.getName() + "\"" ); 208 } 209 writer.write( ">" ); 210 } 211 212 void writeBody( 213 Writer writer, ComponentDirective directive, String pad ) throws IOException 214 { 215 CategoriesDirective categories = directive.getCategoriesDirective(); 216 ContextDirective context = directive.getContextDirective(); 217 PartReference[] parts = directive.getPartReferences(); 218 writeCategoriesDirective( writer, categories, pad ); 219 writeContextDirective( writer, context, pad ); 220 writeParts( writer, parts, pad, false ); 221 } 222 223 private void writeCategoriesDirective( 224 Writer writer, CategoriesDirective categories, String pad ) throws IOException 225 { 226 if( null == categories ) 227 { 228 return; 229 } 230 231 String name = categories.getName(); 232 Priority priority = categories.getPriority(); 233 String target = categories.getTarget(); 234 CategoryDirective[] subCategories = categories.getCategories(); 235 236 if( isaNullValue( name ) && isaNullPriority( priority ) && isaNullValue( target ) 237 && ( subCategories.length == 0 ) ) 238 { 239 return; 240 } 241 242 writer.write( "\n" + pad + "<categories" ); 243 if( !isaNullValue( name ) ) 244 { 245 writer.write( " name=\"" + name + "\"" ); 246 } 247 if( !isaNullPriority( priority ) ) 248 { 249 writer.write( " priority=\"" + priority.getName() + "\"" ); 250 } 251 if( !isaNullValue( target ) ) 252 { 253 writer.write( " target=\"" + target + "\"" ); 254 } 255 if( subCategories.length == 0 ) 256 { 257 writer.write( "/>" ); 258 } 259 else 260 { 261 writer.write( ">" ); 262 for( int i=0; i<subCategories.length; i++ ) 263 { 264 CategoryDirective directive = subCategories[i]; 265 if( directive instanceof CategoriesDirective ) 266 { 267 CategoriesDirective c = (CategoriesDirective) directive; 268 writeCategoriesDirective( writer, c, pad + " " ); 269 } 270 else 271 { 272 writeCategoryDirective( writer, directive, pad + " " ); 273 } 274 } 275 writer.write( "\n" + pad + "</categories>" ); 276 } 277 } 278 279 private boolean isaNullPriority( Priority priority ) 280 { 281 if( null == priority ) 282 { 283 return true; 284 } 285 else 286 { 287 return Priority.DEBUG.equals( priority ); 288 } 289 } 290 291 private boolean isaNullValue( String value ) 292 { 293 if( null == value ) 294 { 295 return true; 296 } 297 else 298 { 299 return "".equals( value ); 300 } 301 } 302 303 private void writeCategoryDirective( 304 Writer writer, CategoryDirective category, String pad ) throws IOException 305 { 306 String name = category.getName(); 307 Priority priority = category.getPriority(); 308 String target = category.getTarget(); 309 310 writer.write( "\n" + pad + "<category" ); 311 if( null != name ) 312 { 313 writer.write( " name=\"" + name + "\"" ); 314 } 315 if( null != priority ) 316 { 317 writer.write( " priority=\"" + priority.getName() + "\"" ); 318 } 319 if( null != target ) 320 { 321 writer.write( " target=\"" + target + "\"" ); 322 } 323 writer.write( "/>" ); 324 } 325 326 private void writeContextDirective( 327 Writer writer, ContextDirective context, String pad ) throws IOException 328 { 329 if( null == context ) 330 { 331 return; 332 } 333 334 String classname = context.getClassname(); 335 PartReference[] parts = context.getDirectives(); 336 337 if( ( null == classname ) && ( parts.length == 0 ) ) 338 { 339 return; 340 } 341 342 writer.write( "\n" + pad + "<context" ); 343 if( null != classname ) 344 { 345 writer.write( " class=\"" + classname + "\"" ); 346 } 347 if( parts.length == 0 ) 348 { 349 writer.write( "/>" ); 350 } 351 else 352 { 353 writer.write( ">" ); 354 writeContextEntries( writer, parts, pad + " " ); 355 writer.write( "\n" + pad + "</context>" ); 356 } 357 } 358 359 /** 360 * Write a collection of part references. 361 * @param writer the writer 362 * @param parts the part refernece array 363 * @param pad the offset 364 * @param flag true if the xml namespace should be included 365 * @exception IOException if an IO error occurs 366 */ 367 protected void writeParts( 368 Writer writer, PartReference[] parts, String pad, boolean flag ) throws IOException 369 { 370 if( null == parts ) 371 { 372 return; 373 } 374 375 if( parts.length == 0 ) 376 { 377 return; 378 } 379 else 380 { 381 writer.write( "\n" + pad + "<parts>" ); 382 writePartReferences( writer, parts, pad + " ", flag ); 383 writer.write( "\n" + pad + "</parts>" ); 384 } 385 } 386 387 private void writePartReferences( 388 Writer writer, PartReference[] parts, String pad, boolean flag ) throws IOException 389 { 390 for( int i=0; i<parts.length; i++ ) 391 { 392 PartReference ref = parts[i]; 393 writePartReference( writer, ref, pad, flag ); 394 } 395 } 396 397 private void writeContextEntries( 398 Writer writer, PartReference[] parts, String pad ) throws IOException 399 { 400 for( int i=0; i<parts.length; i++ ) 401 { 402 PartReference ref = parts[i]; 403 writeContextEntry( writer, ref, pad ); 404 } 405 } 406 407 private void writeContextEntry( 408 Writer writer, PartReference part, String pad ) throws IOException 409 { 410 String key = part.getKey(); 411 if( null == key ) 412 { 413 throw new IllegalStateException( "key" ); 414 } 415 Directive directive = part.getDirective(); 416 if( null == directive ) 417 { 418 throw new IllegalStateException( "directive" ); 419 } 420 if( directive instanceof ValueDirective ) 421 { 422 ValueDirective value = (ValueDirective) directive; 423 writeEntry( writer, key, value, pad ); 424 } 425 else if( directive instanceof LookupDirective ) 426 { 427 LookupDirective value = (LookupDirective) directive; 428 writeLookupEntry( writer, key, value, pad ); 429 } 430 else 431 { 432 String classname = directive.getClass().getName(); 433 final String message = "WARNING: UNRECOGNIZED ENTRY: "+ classname; 434 System.out.println( "# " + message ); 435 System.out.println( "# key: " + key ); 436 System.out.println( "# class: " + classname ); 437 writer.write( "\n" + pad + "<!-- " + message + " -->" ); 438 writer.write( "\n" + pad + "<!-- " ); 439 writer.write( "\n" + pad + "key: " + key ); 440 writer.write( "\n" + pad + "class: " + directive.getClass().getName() ); 441 writer.write( "\n" + pad + "-->" ); 442 } 443 } 444 445 private void writeLookupEntry( 446 Writer writer, String key, LookupDirective directive, String pad ) throws IOException 447 { 448 String classname = directive.getServiceClassname(); 449 writer.write( "\n" + pad + "<entry key=\"" + key + "\" lookup=\"" + classname + "\"/>" ); 450 } 451 452 private void writePartReference( 453 Writer writer, PartReference part, String pad, boolean flag ) throws IOException 454 { 455 String key = part.getKey(); 456 if( null == key ) 457 { 458 throw new IllegalStateException( "key" ); 459 } 460 461 Directive directive = part.getDirective(); 462 if( null == directive ) 463 { 464 throw new IllegalStateException( "directive" ); 465 } 466 if( directive instanceof ComponentDirective ) 467 { 468 ComponentDirective component = (ComponentDirective) directive; 469 writeTaggedComponent( writer, component, key, pad, flag ); 470 } 471 else 472 { 473 String classname = directive.getClass().getName(); 474 final String error = 475 "Part reference directive class not recognized." 476 + "\nClass: " + classname; 477 throw new IllegalArgumentException( error ); 478 } 479 } 480 481 /** 482 * Write a context entry. 483 * @param writer the writer 484 * @param key the entry key 485 * @param value the value directive 486 * @param pad the offset 487 * @exception IOException if an IO error occurs 488 */ 489 protected void writeEntry( Writer writer, String key, ValueDirective value, String pad ) throws IOException 490 { 491 String target = value.getTargetExpression(); 492 String method = value.getMethodName(); 493 494 writer.write( "\n" + pad + "<entry key=\"" + key + "\"" ); 495 if( null != target ) 496 { 497 writer.write( " class=\"" + target + "\"" ); 498 } 499 if( null != method ) 500 { 501 writer.write( " method=\"" + method + "\"" ); 502 } 503 Value[] values = value.getValues(); 504 if( values.length > 0 ) 505 { 506 writer.write( ">" ); 507 VALUE_ENCODER.encodeValues( writer, values, pad + " " ); 508 writer.write( "\n" + pad + "</entry>" ); 509 } 510 else 511 { 512 String v = value.getBaseValue(); 513 if( null != v ) 514 { 515 writer.write( " value=\"" + v + "\"" ); 516 } 517 writer.write( "/>" ); 518 } 519 } 520 }